/*
类与对象  class 类名 { 访问权限： 属性 / 行为 }
访问权限有三种
public 公共权限 类内可以访问 类外可以访问
protected 保护权限 类内可以访问 类外不可以访问 在子类中可以访问
private 私有权限 类内可以访问 类外不可以访问 子类中不能访问
在C++中 struct和class唯一的区别就在于 struct 默认公共权限， class 默认私有权限

C++ 使用构造函数和析构函数处理对象的初始化和清理操作。对象的初始化和清理是编译器必须要求的，
所以如果我们没有提供构造和析构，编译器会自动加上一个空的构造和析构函数。
构造函数语法 类名(){} 没有返回值，也不写void; 函数名与类名相同；构造函数可以有参数，因此可以重载；
程序在调用对象是自动调用构造函数，无需手动调用，而且只会调一次；
析构函数语法 ~类名(){} 在函数名前加个波浪号 其余特性与构造函数一样；

构造函数分类：
  按参数分为：有参数构造函数和无参数构造函数（无参构造又称默认构造）
  按类型分为：普通构造和拷贝构造
  三种调用方式：括号发，显示法，隐式转换法

拷贝构造的调用时机：
  使用一个已经初始化完毕的对象来创建一个新对象
  值传递的方式给函数传参
  以值方式返回局部对象

构造函数调用规则:
  默认情况下，编译器会提供3个函数 默认构造函数，默认析构函数，默认拷贝构造函数(对属性值拷贝)
  如果用户有定义构造函数，不会再提供无参构造函数，但会提供拷贝构造函数
  如果用户有定义拷贝构造函数，不会再提供其他任何构造函数

深拷贝与浅拷贝：
  浅拷贝：简单的赋值拷贝操作
  深拷贝：在堆区重新申请空间，进行拷贝操作
  如果有属性开辟再堆区的，一定要自己提供拷贝构造函数，防止浅拷贝带来的问题

初始化列表：
  c++ 提供了初始化列表语法，用来初始化属性值
  语法： 构造函数(): 属性1(值1), 属性2(值2), ... {}

当类中的成员是其他类的对象时，在构造时先条用对象成员构造，再调用本类构造，析构时相反

静态成员：
  静态成员变量：
    所有对象共享同一份数据
    在编译阶段分配内存
    类内声明，类外初始化
  静态成员函数：
    所有对象共享同一个函数
    静态函数只能访问静态成员变量
  静态成员访问可以通过 对象点 的方式和 类名 的方式， 类名的方式访问不到私有属性中的静态成员

c++中类成员变量和函数分开存储，只有非静态成员变量才属于类的对象上(占对象内存)， 函数也不占对象内存，所有函数共享一个函数实例，
通过 this 指针来区分是通过哪一个对象来调用自己。this 指针是隐含每一个非静态成员函数内的一种指针，不需要定义，直接使用。
this 指针的用途：
  区分同名的形参个成员变量 this->name = name
  在类的非静态成员函数中返回对象本身，可使用 return *this
c++中空指针也可以调用成员函数，如果在函数内有用到 this 就不能用空指针调用了(会报错 所以要兼容 保证代码的健壮性)。

const 修饰成员函数
  常函数，在成员函数后加const，称这个函数为常函数。常函数内不可以修改成员属性，但在成员属性声明时加了关键字mutable后，在常函数内可以修改。
常对象，声明对象前加const，称这个对象为常对象。常对象只能访问常函数。可以访问属性，但不能修改。（mutable 修饰的可以修改）

友元，让一个函数或者类访问另一个类中的私有属性。关键字为 friend
友元有三种实现：
  1.全局函数做友元
  2.类做友元
  3.成员函数做友元

*/

#include <iostream>
#include <string>

using namespace std;

class Persion
{
    // 全局函数做友元，告诉编译器全局函数 frined_fun 是 Persion 类的好朋友，可以访问私有属性
    friend void friend_fun(Persion *persion);
    // 类做友元，告诉编译器类 FriendClass 是 Persion 类的好朋友，可以访问私有属性
    friend class FriendClass;
    // 成员函数做友元，告诉编译器类 FriendClass 的 visit 函数是 Persion 类的好朋友，可以访问私有属性
    // friend void FriendClass::visit();  // 像这样定义就只允许成员函数 visit 访问私有属性

    // 公共权限
public:
    string m_name;
    static int m_a;
    mutable int m_c;
    static int get_ma()
    {
        return m_a;
    }

    // 常函数
    void fun1() const
    {
        // m_a = 100; // 这个是不允许的 只能修改mutable修的是属性
        m_c = 1000; // 这个是可以的
    }
    // 将私有权限暴漏出去
    int get_age() { return m_age; }

    // 无参构造函数
    Persion()
    {
        cout << "This is constructor function (no params)..." << endl;
    }

    // 有参构造函数
    Persion(string name)
    {
        m_name = name;
        cout << "This is constructor function (has params)..." << endl;
    }

    // 拷贝构造函数
    Persion(const Persion &p)
    {
        m_name = p.m_name;
        cout << "This is constructor function (copy)..." << endl;
    }

    // 初始化列表语法
    Persion(string name, int age) : m_name(name), m_age(age)
    {
        cout << "This is constructor function (init list sytanx)..." << endl;
    }

    // 析构函数
    ~Persion()
    {
        cout << "This is destructor function..." << endl;
    }

    // 受保护权限
protected:
    string m_car;

    // 稀有权限
private:
    string m_password;
    int m_age;
    static int m_b; // 静态成员变量 在类外也可以访问
};

// 静态成员变量初始化
int Persion::m_a = 10;
int Persion::m_b = 20;

// 定义全局函数做友元
void friend_fun(Persion *persion)
{
    cout << "gloabl friend func accessing public property m_name = " << persion->m_name << endl;
    cout << "gloabl friend func accessing private property m_age = " << persion->m_age << endl;
}

// 定义类做友元，成员函数做友元
class FriendClass
{
public:
    FriendClass();
    void visit()
    {
        cout << "class friend accessing public property m_name = " << persion->m_name << endl;
        cout << "class frined accessing private property m_age = " << persion->m_age << endl;
    }
    void visit2()  // 这个函数不能访问私有属性
    {
        cout << "class friend accessing public property m_name = " << persion->m_name << endl;
        // cout << "class frined accessing private property m_age = " << persion->m_age << endl;  // 私有属性访问不到
    }
private:
    Persion *persion;
};
FriendClass::FriendClass()
{
    this->persion = new Persion;
}


void func1(Persion p)
{
    cout << "p.name = " << p.m_name << endl;
}

Persion func2()
{
    Persion p1;
    cout << (int *)&p1 << endl;
    return p1;
}

// 构造函数的调用
void call_constructor_func()
{
    // 调用无参构造函数
    Persion p1;

    // 调用有参构造函数
    // 1.括号法 常用
    Persion p2("alias");
    // Persion p2(); // 这是错误的，调用无参构造函数不能加括号，加了编译器认为这是一个函数申明

    // 2.显示法
    Persion p3 = Persion("alias");
    // Persion("alias") 像这样单独写就是匿名对象，当前语句运行结束后立马析构

    // 3.隐式转换法 我用的13.2的gcc 在win上不支持这种转换
    // Persion p4 = "alias";  // 等价于 Persion p4 = Persion("alias");

    // 拷贝构造函数调用时机
    // 1. 用实例化后的对象创建新对象
    Persion p5 = Persion(p2);
    // Persion p5(p4);  // 注意 不能用 拷贝构造函数 初始化匿名对象 编译器会认为是对象声明；

    // 2.值传递的方式给函数传参
    func1(p5);

    // 3.以值的方式返回局部变量
    func2();

    Persion p6("jordan", 19);
    cout << "name = " << p6.m_name << " age = " << p6.get_age() << endl;
}

int main()
{
    call_constructor_func();
    Persion p1;
    // 1.用 点 访问静态成员
    cout << "m_a = " << p1.m_a << endl;
    // p1.m_b = 1000;  // 我的gcc版本不支持访问 私有属性的静态成员
    // cout << "m_b = " << p1.m_b << endl;

    // 2.用 类名 访问静态成员
    // Persion::m_a;
    // Persion::m_b;

    // 常对象
    const Persion p2;
    // p2.m_a; 常对象可以访问不是mutaple修饰的属性，但不能修改
    p2.m_c = 100;
    cout << "m_c = " << p2.m_c << endl;

    // 全局函数友元
    Persion p3("alias", 23);
    friend_fun(&p3);

    // 类做友元
    FriendClass fc;
    fc.visit();

    return 0;
}